package com.googlecode.beanfiles;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class TranslatorMapUtils {

    protected static Log log() {
        return LogFactory.getLog(TranslatorMapUtils.class);
    }

    /**
     * Builds a map of propertyName -> propertyName and propertyName.nested -> propertyName entries for a given collection of property
     * names.
     * 
     * @param propertyNames
     * @return
     */
    public static Map<String, String> buildNestedPropertiesMap(Collection<String> propertyNames) {
        Map<String, String> propertiesMap = new LinkedHashMap<String, String>();
        for (String propertyName : propertyNames) {
            String[] splitResult = propertyName.split("\\.");
            if (splitResult.length > 1) {
                // assign nested property index
                propertiesMap.put(propertyName, splitResult[0]);
            } else {
                propertiesMap.put(propertyName, propertyName);
            }
        }
        return propertiesMap;
    }

    /**
     * Constructs a List of property names (including nested properties) from a class.
     * 
     * Currently avoids the properties (class, empty, bytes). This is a convenience utility, that should not be considered "safe" for all types
     * of beans, since other properties might need to be avoided as well.
     * 
     * @param <C>
     * @param clazz
     * @return
     */
    public static <C> List<String> buildNestedPropertiesList(Class<C> clazz) {
        List<String> properties = new ArrayList<String>();
        try {
            PropertyDescriptor[] descriptors = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
            for (PropertyDescriptor descriptor : descriptors) {
                if (!descriptor.getName().equals("class") && !descriptor.getName().equals("empty") && !descriptor.getName().equals("bytes")) {
                    List<String> nestedProperties = buildNestedPropertiesList(descriptor.getPropertyType());
                    if (nestedProperties.size() == 0) {
                        properties.add(descriptor.getName());
                    } else {
                        for (String nestedProperty : nestedProperties) {
                            properties.add(descriptor.getName() + "." + nestedProperty);
                        }
                    }
                }
            }
        } catch (IntrospectionException e) {
            log().error(e);
            throw new TranslationException("failed building nested properties map for class " + clazz.getName());
        }
        return properties;
    }

    public static Map<String, String> buildNestedPropertiesMap(Map<String, String> reorderMap, List<String> streamHeader) {
        Map<String, String> propertiesMap = new LinkedHashMap<String, String>();
        for (String streamHeading : streamHeader) {
            String propertyName = reorderMap.get(streamHeading);
            String[] splitResult = propertyName.split("\\.");
            if (splitResult.length > 1) {
                // assign nested property index
                propertiesMap.put(propertyName, splitResult[0]);
            } else {
                propertiesMap.put(propertyName, propertyName);
            }
        }
        return propertiesMap;
    }

}
